/**
 * <copyright>
 * </copyright>
 *
 * $Id$
 */
package ccore.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.NotificationChain;
import org.eclipse.emf.common.util.EList;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.impl.EObjectImpl;
import org.eclipse.emf.ecore.util.EObjectContainmentEList;
import org.eclipse.emf.ecore.util.InternalEList;

import ccore.CCore;
import ccore.CcorePackage;
import ccore.constraint.CConstraint;
import ccore.constraint.CEq;
import ccore.constraint.CLeq;
import ccore.constraint.ConstraintFactory;
import ccore.expression.CIntCst;
import ccore.expression.CIntVarRef;
import ccore.expression.ExpressionFactory;
import ccore.instance.CObject;
import ccore.model.CClass;
import choco.Problem;

/**
 * <!-- begin-user-doc -->
 * An implementation of the model object '<em><b>CCore</b></em>'.
 * <!-- end-user-doc -->
 * <p>
 * The following features are implemented:
 * <ul>
 *   <li>{@link ccore.impl.CCoreImpl#getCClasses <em>CClasses</em>}</li>
 *   <li>{@link ccore.impl.CCoreImpl#getCConstraints <em>CConstraints</em>}</li>
 *   <li>{@link ccore.impl.CCoreImpl#getCObjects <em>CObjects</em>}</li>
 *   <li>{@link ccore.impl.CCoreImpl#getName <em>Name</em>}</li>
 *   <li>{@link ccore.impl.CCoreImpl#getInfinite <em>Infinite</em>}</li>
 *   <li>{@link ccore.impl.CCoreImpl#getProblem <em>Problem</em>}</li>
 *   <li>{@link ccore.impl.CCoreImpl#getGeneratedConstraints <em>Generated Constraints</em>}</li>
 * </ul>
 * </p>
 *
 * @generated
 */
public class CCoreImpl extends EObjectImpl implements CCore {
	/**
	 * The cached value of the '{@link #getCClasses() <em>CClasses</em>}' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getCClasses()
	 * @generated
	 * @ordered
	 */
	protected EList<CClass> cClasses;

	/**
	 * The cached value of the '{@link #getCConstraints() <em>CConstraints</em>}' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getCConstraints()
	 * @generated
	 * @ordered
	 */
	protected EList<CConstraint> cConstraints;

	/**
	 * The cached value of the '{@link #getCObjects() <em>CObjects</em>}' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getCObjects()
	 * @generated
	 * @ordered
	 */
	protected EList<CObject> cObjects;

	/**
	 * The default value of the '{@link #getName() <em>Name</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getName()
	 * @generated
	 * @ordered
	 */
	protected static final String NAME_EDEFAULT = null;

	/**
	 * The cached value of the '{@link #getName() <em>Name</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getName()
	 * @generated
	 * @ordered
	 */
	protected String name = NAME_EDEFAULT;

	/**
	 * The default value of the '{@link #getInfinite() <em>Infinite</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getInfinite()
	 * @generated
	 * @ordered
	 */
	protected static final int INFINITE_EDEFAULT = 1000000;

	/**
	 * The cached value of the '{@link #getInfinite() <em>Infinite</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getInfinite()
	 * @generated
	 * @ordered
	 */
	protected int infinite = INFINITE_EDEFAULT;

	/**
	 * The default value of the '{@link #getProblem() <em>Problem</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getProblem()
	 * @generated
	 * @ordered
	 */
	protected static final Problem PROBLEM_EDEFAULT = null;

	/**
	 * The cached value of the '{@link #getProblem() <em>Problem</em>}' attribute.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getProblem()
	 * @generated
	 * @ordered
	 */
	protected Problem problem = PROBLEM_EDEFAULT;

	/**
	 * The cached value of the '{@link #getGeneratedConstraints() <em>Generated Constraints</em>}' containment reference list.
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @see #getGeneratedConstraints()
	 * @generated
	 * @ordered
	 */
	protected EList<CConstraint> generatedConstraints;

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	protected CCoreImpl() {
		super();
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	protected EClass eStaticClass() {
		return CcorePackage.Literals.CCORE;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public EList<CClass> getCClasses() {
		if (cClasses == null) {
			cClasses = new EObjectContainmentEList<CClass>(CClass.class, this, CcorePackage.CCORE__CCLASSES);
		}
		return cClasses;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public EList<CConstraint> getCConstraints() {
		if (cConstraints == null) {
			cConstraints = new EObjectContainmentEList<CConstraint>(CConstraint.class, this, CcorePackage.CCORE__CCONSTRAINTS);
		}
		return cConstraints;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public EList<CObject> getCObjects() {
		if (cObjects == null) {
			cObjects = new EObjectContainmentEList<CObject>(CObject.class, this, CcorePackage.CCORE__COBJECTS);
		}
		return cObjects;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public int getInfinite() {
		return infinite;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setInfinite(int newInfinite) {
		int oldInfinite = infinite;
		infinite = newInfinite;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, CcorePackage.CCORE__INFINITE, oldInfinite, infinite));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public Problem getProblem() {
		return problem;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setProblem(Problem newProblem) {
		Problem oldProblem = problem;
		problem = newProblem;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, CcorePackage.CCORE__PROBLEM, oldProblem, problem));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public EList<CConstraint> getGeneratedConstraints() {
		if (generatedConstraints == null) {
			generatedConstraints = new EObjectContainmentEList<CConstraint>(CConstraint.class, this, CcorePackage.CCORE__GENERATED_CONSTRAINTS);
		}
		return generatedConstraints;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @not generated
	 */
	public void initNodes() {
		// classes
		for (CClass c: getCClasses()) {
			c.initCCore(this);
		}
		
		// objects
		for (CObject o: getCObjects()) {
			o.initCCore(this);
		}
		
		// constraints
		for (CConstraint c: getCConstraints()) {
			c.initCCore(this);
		}
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @not generated
	 */
	public void initClassId() {
		List<CClass> superClasses = new ArrayList<CClass>();
		
		for (CClass c: getCClasses()) {
			if (c.getSuperClass() == null)
				superClasses.add(c);
		}
		
		int id = 1;
		for (CClass c: superClasses) {
			id = c.initId(id) + 1;
		}
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @not generated
	 */
	public void initClassCardinality() {
		for (CClass c: getCClasses()) {
			c.getCardinality().setName(c.getName() + " card");
			c.getCardinality().setValue(-1);
			c.getCardinality().setLowerBound(0);
			c.getCardinality().setUpperBound(getInfinite());

			c.getSelfCardinality().setName(c.getName() + " self card");
			c.getSelfCardinality().setValue(-1);
			c.getSelfCardinality().setLowerBound(0);
			c.getSelfCardinality().setUpperBound(getInfinite());
		}
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @not generated
	 */
	public void initConstraintsVariables() {
		for (CConstraint c: getCConstraints()) {
			c.initVariables();
		}
		for (CConstraint c: getGeneratedConstraints()) {
			c.initVariables();
		}
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @not generated
	 */
	public void addGeneratedConstraints() {
		getGeneratedConstraints().clear();
		
		/*
		 * Classes Cardinalities
		 */
		for (CClass c: getCClasses()) {
			// C.isAbstract -> |C*| = 0
			if (c.isAbstract()) {
				CConstraint cst = ConstraintFactory.eINSTANCE.createCConstraint();
				cst.setName("|" + c.getName() + "*| = 0");
				
				CEq eq = ConstraintFactory.eINSTANCE.createCEq();
				cst.setConstraint(eq);
				CIntVarRef ref = ExpressionFactory.eINSTANCE.createCIntVarRef();
				ref.setVariable(c.getSelfCardinality());
				eq.getParameters().add(ref);
				CIntCst val = ExpressionFactory.eINSTANCE.createCIntCst();
				val.setValue(0);
				eq.getParameters().add(val);
				
				getGeneratedConstraints().add(cst);
			}
			// |C| = |C*| + sum ( S: C.subclasses | |S| )
			{
				CConstraint cst = ConstraintFactory.eINSTANCE.createCConstraint();
				cst.setName("|" + c.getName() + "| = |" + c.getName() + "*|");
				for (CClass sc: c.getSubClasses()) {
					cst.setName(cst.getName() + " + |" + sc.getName() + "|");
				}
				// TODO
				
				getGeneratedConstraints().add(cst);
			}
			// |C*| >= C.objects.size
			if (c.getCObjects().size() > 0) {
				CConstraint cst = ConstraintFactory.eINSTANCE.createCConstraint();
				cst.setName("|" + c.getName() + "*| >= objects number (" + c.getCObjects().size() + ")");
				
				CLeq leq = ConstraintFactory.eINSTANCE.createCLeq();
				cst.setConstraint(leq);
				CIntVarRef ref = ExpressionFactory.eINSTANCE.createCIntVarRef();
				ref.setVariable(c.getSelfCardinality());
				leq.getParameters().add(ref);
				CIntCst val = ExpressionFactory.eINSTANCE.createCIntCst();
				val.setValue(c.getCObjects().size());
				leq.getParameters().add(val);
				
				getGeneratedConstraints().add(cst);
			}
		}
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public String getName() {
		return name;
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	public void setName(String newName) {
		String oldName = name;
		name = newName;
		if (eNotificationRequired())
			eNotify(new ENotificationImpl(this, Notification.SET, CcorePackage.CCORE__NAME, oldName, name));
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public NotificationChain eInverseRemove(InternalEObject otherEnd, int featureID, NotificationChain msgs) {
		switch (featureID) {
			case CcorePackage.CCORE__CCLASSES:
				return ((InternalEList<?>)getCClasses()).basicRemove(otherEnd, msgs);
			case CcorePackage.CCORE__CCONSTRAINTS:
				return ((InternalEList<?>)getCConstraints()).basicRemove(otherEnd, msgs);
			case CcorePackage.CCORE__COBJECTS:
				return ((InternalEList<?>)getCObjects()).basicRemove(otherEnd, msgs);
			case CcorePackage.CCORE__GENERATED_CONSTRAINTS:
				return ((InternalEList<?>)getGeneratedConstraints()).basicRemove(otherEnd, msgs);
		}
		return super.eInverseRemove(otherEnd, featureID, msgs);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public Object eGet(int featureID, boolean resolve, boolean coreType) {
		switch (featureID) {
			case CcorePackage.CCORE__CCLASSES:
				return getCClasses();
			case CcorePackage.CCORE__CCONSTRAINTS:
				return getCConstraints();
			case CcorePackage.CCORE__COBJECTS:
				return getCObjects();
			case CcorePackage.CCORE__NAME:
				return getName();
			case CcorePackage.CCORE__INFINITE:
				return new Integer(getInfinite());
			case CcorePackage.CCORE__PROBLEM:
				return getProblem();
			case CcorePackage.CCORE__GENERATED_CONSTRAINTS:
				return getGeneratedConstraints();
		}
		return super.eGet(featureID, resolve, coreType);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@SuppressWarnings("unchecked")
	@Override
	public void eSet(int featureID, Object newValue) {
		switch (featureID) {
			case CcorePackage.CCORE__CCLASSES:
				getCClasses().clear();
				getCClasses().addAll((Collection<? extends CClass>)newValue);
				return;
			case CcorePackage.CCORE__CCONSTRAINTS:
				getCConstraints().clear();
				getCConstraints().addAll((Collection<? extends CConstraint>)newValue);
				return;
			case CcorePackage.CCORE__COBJECTS:
				getCObjects().clear();
				getCObjects().addAll((Collection<? extends CObject>)newValue);
				return;
			case CcorePackage.CCORE__NAME:
				setName((String)newValue);
				return;
			case CcorePackage.CCORE__INFINITE:
				setInfinite(((Integer)newValue).intValue());
				return;
			case CcorePackage.CCORE__PROBLEM:
				setProblem((Problem)newValue);
				return;
			case CcorePackage.CCORE__GENERATED_CONSTRAINTS:
				getGeneratedConstraints().clear();
				getGeneratedConstraints().addAll((Collection<? extends CConstraint>)newValue);
				return;
		}
		super.eSet(featureID, newValue);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public void eUnset(int featureID) {
		switch (featureID) {
			case CcorePackage.CCORE__CCLASSES:
				getCClasses().clear();
				return;
			case CcorePackage.CCORE__CCONSTRAINTS:
				getCConstraints().clear();
				return;
			case CcorePackage.CCORE__COBJECTS:
				getCObjects().clear();
				return;
			case CcorePackage.CCORE__NAME:
				setName(NAME_EDEFAULT);
				return;
			case CcorePackage.CCORE__INFINITE:
				setInfinite(INFINITE_EDEFAULT);
				return;
			case CcorePackage.CCORE__PROBLEM:
				setProblem(PROBLEM_EDEFAULT);
				return;
			case CcorePackage.CCORE__GENERATED_CONSTRAINTS:
				getGeneratedConstraints().clear();
				return;
		}
		super.eUnset(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public boolean eIsSet(int featureID) {
		switch (featureID) {
			case CcorePackage.CCORE__CCLASSES:
				return cClasses != null && !cClasses.isEmpty();
			case CcorePackage.CCORE__CCONSTRAINTS:
				return cConstraints != null && !cConstraints.isEmpty();
			case CcorePackage.CCORE__COBJECTS:
				return cObjects != null && !cObjects.isEmpty();
			case CcorePackage.CCORE__NAME:
				return NAME_EDEFAULT == null ? name != null : !NAME_EDEFAULT.equals(name);
			case CcorePackage.CCORE__INFINITE:
				return infinite != INFINITE_EDEFAULT;
			case CcorePackage.CCORE__PROBLEM:
				return PROBLEM_EDEFAULT == null ? problem != null : !PROBLEM_EDEFAULT.equals(problem);
			case CcorePackage.CCORE__GENERATED_CONSTRAINTS:
				return generatedConstraints != null && !generatedConstraints.isEmpty();
		}
		return super.eIsSet(featureID);
	}

	/**
	 * <!-- begin-user-doc -->
	 * <!-- end-user-doc -->
	 * @generated
	 */
	@Override
	public String toString() {
		if (eIsProxy()) return super.toString();

		StringBuffer result = new StringBuffer(super.toString());
		result.append(" (name: ");
		result.append(name);
		result.append(", infinite: ");
		result.append(infinite);
		result.append(", problem: ");
		result.append(problem);
		result.append(')');
		return result.toString();
	}

} //CCoreImpl
